package com.xyzm.mytest.nio;

import org.junit.Test;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.SortedMap;

/**
 * 一、通道（channel）：用于源节点与目标节点的连接，在Java NIO中负责缓冲区中的数据传输。channel本身不存储数据，因此需要配合缓冲区进行传输。
 * 二、通道的主要实现类
 * 　java.nio.channels.Channel接口
 *           |--FileChannel
 *           |--SocketChannel
 *           |--ServerSocketChannel
 *           |--DatagramChannel
 * 三、获取通道
 * 1、Java  针对各个通道提供的 getChannel()方法
 *      本地
 *          FileInputStream/FileOutputStream
 *          RandomAccessFile
 *      网络
 *      Socket
 *      ServerSocket
 *      DatagramSocket
 *2、在JDK1.7中的NIO.2 针对各个通道提供了静态方法open()
 *3、在JDK1.7中的NIO.2 的Files工具类的newByteChannel()
 * 四、通道之间的数据传输
 *  transferFrom
 *  transferTo
 * 五、分散（Scatter）与聚集（Gather）
 * 分散读取（Scattering Reads）将通道中的数据分散到多个缓存区中
 * 聚集写入（Gathering Writes）将多个缓存区中的数据据街道通道中
 * 六、字符集
 * 编码：字符串->字节数组
 * 解码：字节数组->字符串
 */
public class TestChannel {
    @Test
    public void test6() throws CharacterCodingException {
        Charset gbk = Charset.forName("GBK");
        //解码器
        CharsetDecoder decoder = gbk.newDecoder();
        //编码器
        CharsetEncoder encoder = gbk.newEncoder();
        CharBuffer charBuffer = CharBuffer.allocate(1024);
        String str = "Hello World!";
        charBuffer.put(str);
        charBuffer.flip();
        ByteBuffer byteBuffer = encoder.encode(charBuffer);
        for (int i = 0; i < byteBuffer.limit(); i++) {
            System.out.println(byteBuffer.get());
        }
        byteBuffer.flip();
        //解码
        CharBuffer charBuffer1 = decoder.decode(byteBuffer);
        System.out.println(charBuffer1.toString());

    }
    @Test
    public void test5(){
        SortedMap<String, Charset> availableCharsets = Charset.availableCharsets();
        availableCharsets.forEach(((s, charset) -> {
            System.out.println(s+":\t"+charset);
        }));
    }
    //1、利用通道完成文件复制（非直接缓冲区）
    @Test
    public void test1(){
        long start = System.currentTimeMillis();
        FileInputStream fileInputStream = null;
        FileOutputStream fileOutputStream = null;
        FileChannel fileInputStreamChannel = null;
        FileChannel fileOutputStreamChannel = null;
        try {
            fileInputStream = new FileInputStream(new File("E:\\MyTest\\loginBg.jpg"));
            fileOutputStream = new FileOutputStream(new File("E:\\MyTest\\loginBg-back.jpg"));

            fileInputStreamChannel = fileInputStream.getChannel();
            fileOutputStreamChannel = fileOutputStream.getChannel();
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            while (fileInputStreamChannel.read(byteBuffer) != -1){
                byteBuffer.flip();
                fileOutputStreamChannel.write(byteBuffer);
                byteBuffer.clear();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if(fileOutputStreamChannel != null){
                    fileOutputStreamChannel.close();
                }
                if(fileOutputStreamChannel != null){
                    fileInputStreamChannel.close();
                }
                if(fileOutputStream != null){
                    fileOutputStream.close();
                }
                if(fileInputStream != null){
                    fileInputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        long end = System.currentTimeMillis();
        System.out.println(end-start);
    }
    //2、利用通道完成文件复制（直接缓冲区、内存映射文件）
    @Test
    public void test2() throws IOException {
        long start = System.currentTimeMillis();
        FileChannel inChannel = FileChannel.open(Paths.get("E:\\MyTest\\loginBg.jpg"), StandardOpenOption.READ);
        FileChannel outChannel = FileChannel.open(Paths.get("E:\\MyTest\\loginBg-Mapper.jpg"), StandardOpenOption.WRITE, StandardOpenOption.READ,StandardOpenOption.CREATE);
        //内存映射文件
        MappedByteBuffer inMapperBuffer = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
        MappedByteBuffer outMapperBuffer = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());
        //直接对缓冲区进行读写
        byte[] dst = new byte[inMapperBuffer.limit()];
        inMapperBuffer.get(dst);
        outMapperBuffer.put(dst);
        outChannel.close();
        inChannel.close();
        long end = System.currentTimeMillis();
        System.out.println(end-start);
    }
    //通道之间直接传输数据
    @Test
    public void test3() throws IOException {
        FileChannel inChannel = FileChannel.open(Paths.get("E:\\MyTest\\loginBg.jpg"), StandardOpenOption.READ);
        FileChannel outChannel = FileChannel.open(Paths.get("E:\\MyTest\\loginBg-Transfer.jpg"), StandardOpenOption.WRITE, StandardOpenOption.READ,StandardOpenOption.CREATE);
        //inChannel.transferTo(0,inChannel.size(),outChannel);
        outChannel.transferFrom(inChannel,0,inChannel.size());
    }
    @Test
    public void test4() throws IOException {
        RandomAccessFile randomAccessFile = new RandomAccessFile("E:\\MyTest\\孙悟空.txt", "rw");
        FileChannel randomAccessFileChannel = randomAccessFile.getChannel();
        ByteBuffer buf1 = ByteBuffer.allocate(100);
        ByteBuffer buf2 = ByteBuffer.allocate(1024);
        ByteBuffer[] bufs = {buf1,buf2};
        randomAccessFileChannel.read(bufs);
        for (ByteBuffer buf : bufs) {
            buf.flip();
        }
        System.out.println(new String(bufs[0].array(),0,bufs[0].limit()));
        System.out.println("==================================================");
        System.out.println(new String(bufs[1].array(),0,bufs[1].limit()));
        //聚集写入
        RandomAccessFile rw = new RandomAccessFile("E:\\MyTest\\sunwukong.txt", "rw");
        FileChannel rwChannel = rw.getChannel();
        rwChannel.write(bufs);
        rwChannel.close();
        randomAccessFileChannel.close();
    }
}
